// src/user/user.service.ts
import { Model } from 'mongoose';
import * as bcrypt from 'bcrypt';
import { firstValueFrom } from 'rxjs';
import { Injectable, Inject, LoggerService } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { User, UserDocument } from './schemas/user.schema';
import {
  CreateUserByEmailDto,
  CheckPhoneNumberForGenVeriCode,
} from './dto/create-user.dto';
import {
  ErrorResponseException,
  ValidationException,
} from '../common/exceptions/custom-exception';
import { HttpService } from '@nestjs/axios';
import configuration from '@/config/configuration';

interface GitHubUserResp {
  id: number;
  login: string;
  name: string;
  avatar_url: string;
  email: string;
}

@Injectable()
export class UserService {
  constructor(
    @InjectModel(User.name) private userModel: Model<UserDocument>,
    @Inject('CustomLogger') private readonly logger: LoggerService,
    private readonly httpService: HttpService,
  ) {}

  async createByEmail(
    createUserByEmailDto: CreateUserByEmailDto,
  ): Promise<User> {
    const saltRounds = 10;
    try {
      const createdUser = new this.userModel({
        username: createUserByEmailDto.username,
        password: await bcrypt.hash(createUserByEmailDto.password, saltRounds),
      });
      return await createdUser.save();
    } catch (error) {
      this.logger.error(error);
      if (error.code === 11000) {
        throw new ErrorResponseException({
          errno: 10001,
          message: 'username 重复；',
        });
      }
      throw new ErrorResponseException({
        errno: 500,
        message: 'Internal server error',
      });
    }
  }

  // 不验证密码
  async IsUserExist(username: string): Promise<boolean> {
    const findedUser = await this.userModel.findOne({
      username: username,
    });
    return Boolean(findedUser);
  }
  // 验证密码
  async findByUsername(username: string, password: string) {
    const findedUser = await this.userModel.findOne({
      username: username,
    });
    if (!findedUser) {
      return null;
    }
    // 验证密码
    const verifyPws = await bcrypt.compare(password, findedUser.password);
    if (!verifyPws) {
      throw new ValidationException('loginCheckFailInfo');
    }
    return findedUser.toJSON();
  }

  async findById(id: string): Promise<User> {
    const findedUser = await this.userModel.findOne({ id: id });
    return findedUser.toJSON();
  }

  async createByPhoneNumber({
    phoneNumber,
  }: CheckPhoneNumberForGenVeriCode): Promise<User> {
    try {
      const createdUser = new this.userModel({
        username: phoneNumber,
        phoneNumber,
        type: 'cellphone',
      });
      return await createdUser.save();
    } catch (error) {
      this.logger.error(error);
      if (error.code === 11000) {
        throw new ErrorResponseException({
          errno: 10001,
          message: 'username 重复；',
        });
      }
      throw new ErrorResponseException({
        errno: 500,
        message: 'Internal server error',
      });
    }
  }

  async findByPhoneNumber({ phoneNumber }: CheckPhoneNumberForGenVeriCode) {
    const findedUser = (
      await this.userModel.findOne({ username: phoneNumber })
    ).toJSON();
    if (!findedUser) {
      return null;
    }
    return findedUser;
  }

  async getGitHubOauthAccessToken(code: string) {
    const config = configuration().Oauth2.github;
    const { data } = await firstValueFrom(
      this.httpService.post(
        config.getTokenUrl,
        {
          client_id: config.client_id,
          client_secret: config.client_secret,
          code,
          redirect_uri: config.redirectUrl,
        },
        {
          headers: {
            Accept: 'application/json',
          },
        },
      ),
    );
    return data.access_token;
  }

  async getGitHubUserData(access_token: string) {
    const config = configuration().Oauth2.github;
    const { data } = await firstValueFrom(
      this.httpService.get<GitHubUserResp>(config.apiBaseUrl, {
        headers: {
          Accept: 'application/json',
          Authorization: `Bearer ${access_token}`,
        },
      }),
    );
    return data;
  }

  async loginByGithub(code: string) {
    const access_token = await this.getGitHubOauthAccessToken(code);
    const { id, name, avatar_url } = await this.getGitHubUserData(access_token);
    const stringId = id.toString();
    const username = `Github${stringId}`;
    // Gitee+id
    // Github+id
    // WX+id
    const existUser = await this.IsUserExist(username);
    if (existUser) {
      return (await this.userModel.findOne({ username })).toJSON();
    } else {
      const createdUser = new this.userModel({
        username,
        nickName: name,
        picture: avatar_url,
        type: 'oauth',
        provider: 'github',
        oauthID: stringId,
      });
      return (await createdUser.save()).toJSON();
    }
  }
}
